#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>

#include "list_char.h"

#define BUFFER_SIZE 	100
#define PORT 		2000

enum {SHORT = 1, LONG = 2};

//Obtiene y retorna la respuesta del servidor
char* retServerResponse(int sockfd) {

	LIST_CHAR* list = list_create();
	char c;
	read(sockfd, &c, 1);
	while (c != '\n') {
		list_add(list, c);
		read(sockfd, &c, 1);
	}

	char* ret = getString(list);
	//list_show(list);
	list_kill(list);

	return ret;
}

///Obtiene una linea de respuesta del servidor
void getServerResponse(int sockfd) {
	char* ret = retServerResponse(sockfd);
	printf("%s\n", ret);	
	free(ret);	
}



void executeWC(char* path, int sockfd)
{
	//Leo el archivo
	FILE* file = fopen(path, "r");

	LIST_CHAR* list = list_create();
	char c = fgetc(file);
	while (c != EOF) {
		list_add(list, c);
		c = fgetc(file);
	}

	char* message = getString(list);
	int length = list->count;
	
	list_kill(list);
	fclose(file);

	//envío archivo al servidor

	//envio w1234
	write(sockfd, "w", 1);
	LIST_CHAR* list_num = list_create_from_int(length);
	write(sockfd, getString(list_num), list_num->count);
	list_kill(list_num);
	write(sockfd, "\n", 1);

	//envio texto
	write(sockfd, message, length);
	write(sockfd, "\n", 1);

	//obtengo la respuesta
	printf("wc = ");
	getServerResponse(sockfd);
}

void send_command_to_server(char* command,int sockfd, int flag){
	write(sockfd,&command[0],1);
	if(flag == LONG){
		int i;
		for (i = 2; i < BUFFER_SIZE && command[i] != '\0'; i++)
			write(sockfd, &command[i], 1);
	}
	write(sockfd,"\n",1);
}

///Hace el rol de multiplexor con command[0] el caracter que seleciona el camino. El switch en C tiene fallthrough.
void send_to_server_and_get_response(char* command, int sockfd) {
	int num, i, length,current_length;
	char* ret_aux;

	switch (command[0]) {
		
		///Maneja quit (debe ser aparte de load porque la respuesta 'q' no se debe mostrar en consola)
		case 'q':
			send_command_to_server(command,sockfd,SHORT);
			free(retServerResponse(sockfd));
			return;	

		///Maneja load
		case 'l':
			send_command_to_server(command,sockfd,SHORT);
			getServerResponse(sockfd);
			return;

		///Maneja ping
		case 'p':
			sscanf(command, "p %d", &num);
			send_command_to_server(command,sockfd,LONG);
			for(i=1;i<=num;i++)
				getServerResponse(sockfd);
			return;

		///Maneja fortune
		case 'f':
			send_command_to_server(command,sockfd,SHORT);

			while(1)
			{
				char* num = retServerResponse(sockfd);
				int i = atoi(num);
				free(num);
				if(i == -1)	return;
				getServerResponse(sockfd);
			}

		///Maneja wc
		case 'w':
			executeWC(command + 2, sockfd);
			return;
		
		///Maneja cat		
		case 'c':
			send_command_to_server(command,sockfd,LONG);

			length = 0;
			current_length = 0;			
			ret_aux = retServerResponse(sockfd);
			sscanf(ret_aux,"%d",&length);

			while(current_length<length){
				ret_aux = retServerResponse(sockfd);
				printf("%s\n",ret_aux);
				current_length += strlen(ret_aux);
				current_length++;			
			}
			
			return;
		default:
			printf("?\n");
			return;
	}

}

int main(int argc, char *argv[]) {

	//Auxiliary variables
	int sockfd, len, result;
	struct sockaddr_in address;
	char* message = (char*) malloc(BUFFER_SIZE);

	//commands
	printf("q\t(quit)\n");
	printf("p n\t(ping de n paquetes)\n");
	printf("l\t(carga del servidor)\n");
	printf("f\t(fortuna)\n");
	printf("c archivo\t(cat de archivo)\n");
	printf("w archivo\t(wc de archivo)\n");

	//create connection
	sockfd = socket(AF_INET, SOCK_STREAM, 0);
	address.sin_family = AF_INET; //IPv4
	address.sin_addr.s_addr = inet_addr(argv[1]);
	address.sin_port = htons(PORT);
	len = sizeof(address);
	/* Now connect our socket to the server's socket. */
	result = connect(sockfd, (struct sockaddr *) &address, len);

	if (result == -1) {
		perror("Error: lost connection");
		exit(1);
	}

	while (*message != 'q') {
		printf("> ");
		//fgets(message,sizeof(message),stdin);
		gets(message);
		send_to_server_and_get_response(message, sockfd);
	}

	//close socket
	close(sockfd);

	exit(0);
}

